package tinybase.sm;

import java.io.PrintStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import tinybase.basic.RedBase;
import tinybase.basic.RedBase.AttrType;

public class Printer {
	
	public static final int MAXPRINTSTRING = 2 * RedBase.MAXNAME + 5;
	//
	// ------------------------------------------------------------------------------
	//

	//
	// Printer
	//
	// This class handles the printing of tuples.
	//
	//  DataAttrInfo - describes all of the attributes. Defined
	//	      within sm.h
	//  attrCount - the number of attributes
	//
	public Printer(DataAttrInfo[] attributes_, int attrCount_) {
	    attrCount = attrCount_;
	    attributes = new DataAttrInfo[attrCount];

	    for (int i=0; i < attrCount; i++)
	        attributes[i] = attributes_[i];

	    // Number of tuples printed
	    iCount = 0;

	    // Figure out what the header information will look like.  Normally,
	    // we can just use the attribute name, but if that appears more than
	    // once, then we should use "relation.attribute".

	    // this line broke when using CC
	    // changing to use malloc and free instead of new and delete
	    // psHeader = (char **) new (char *)[attrCount];
	    psHeader= new LinkedList<String>();
	    
	    // Also figure out the number of spaces between each attribute
	    spaces = new int[attrCount];

	    for (int i=0; i < attrCount; i++ ) {
	        // Try to find the attribute in another column
	        boolean bFound = false;
	        byte[] tmp= new byte[MAXPRINTSTRING];
	        Arrays.fill(tmp, (byte)'0');
	        psHeader.add(i, tmp.toString());
	        
	        for (int j=0; j < attrCount; j++)
	            if (j != i && attributes[i].attrName.equals(attributes[j].attrName)) {
	                bFound = true;
	                break;
	            }

	        if (bFound)
	        	psHeader.set(i, attributes[i].relName + "." + attributes[i].attrName);
	        else
	        	psHeader.set(i, attributes[i].attrName);

	        if (attributes[i].attrType == AttrType.STRING)
	            spaces[i] = Math.min(attributes[i].attrLength, MAXPRINTSTRING);
	        else
	            spaces[i] = Math.max(12, psHeader.get(i).length());

	        // We must subtract out those characters that will be for the
	        // header.
	        spaces[i] -= psHeader.get(i).length();

	        // If there are negative (or zero) spaces, then insert a single
	        // space.
	        if (spaces[i] < 1) {
	            // The psHeader will give us the space we need
	            spaces[i] = 0;
	            psHeader.set(i, psHeader.get(i) + " ");
	        }
	    }

	}

	//
	// void Spaces(int maxLength, int printedSoFar)
	//
	// This method will output some spaces so that print entry will align
	// everything
	// nice and neat.
	//
	void spaces(int maxLength, int printedSoFar)
	{
	    for (int i = printedSoFar; i < maxLength; i++)
	        System.out.println(" ");
	}

	//
	// PrintHeader
	//
	public void printHeader(PrintStream c) {
		int dashes = 0;
	    int iLen= 0;
	    int i,j;

	    for (i = 0; i < attrCount; i++) {
	        // Print out the header information name
	    	c.print(psHeader.get(i));
	    	iLen = psHeader.get(i).length();
	        dashes += iLen;

	        for (j = 0; j < spaces[i]; j++)
	            c.print(" ");

	        dashes += spaces[i];
	    }

	    c.println();
	    for (i = 0; i < dashes; i++) c.print("-");
	    c.println();

	}

	// Two flavors for the Print routine. The first takes a char* to the
	// data and is useful when the data corresponds to a single record in
	// a table -- since in this situation you can just send in the
	// RecData. The second will be useful in the QL layer.
	public void print(PrintStream c, byte[] data) {
		byte[] str= new byte[MAXPRINTSTRING];
		byte[] strSpace= new byte[50];
		int i, a;
		float b;
		
		if (data == null)
	        return;

	    // Increment the number of tuples printed
	    iCount++;

	    for (i = 0; i<attrCount; i++) {
	        if (attributes[i].attrType == AttrType.STRING) {
	            // We will only print out the first MAXNAME+10 characters of
	            // the string value.
	        	Arrays.fill(str, (byte)'0');

	            if (attributes[i].attrLength > MAXPRINTSTRING) {
	            	System.arraycopy(data, attributes[i].offset, str, 0, MAXPRINTSTRING-1);
	                str[MAXPRINTSTRING-3] ='.';
	                str[MAXPRINTSTRING-2] ='.';
	                c.print(str);
	                spaces(MAXPRINTSTRING, str.length);
	            } else {
	            	System.arraycopy(data, attributes[i].offset, str, 0, attributes[i].attrLength);
	            	c.print(str);
	            	if (attributes[i].attrLength < psHeader.get(i).length())
	            		spaces(psHeader.get(i).length(), str.length);
	            	else
	            		spaces(attributes[i].attrLength, str.length);
	            }   
	        }   
	        if (attributes[i].attrType == AttrType.INT) {
	        	a= ByteBuffer.wrap(data).getInt(attributes[i].offset);
	        	ByteBuffer.wrap(strSpace).putInt(a);
	        	c.print(a);
	        	if (psHeader.get(i).length() < 12)
	        		spaces(12, strSpace.length);
	        	else
	        		spaces(psHeader.get(i).length(), strSpace.length);
	        }
	        if (attributes[i].attrType == AttrType.FLOAT) {
	        	b= ByteBuffer.wrap(data).getFloat(attributes[i].offset);
	        	ByteBuffer.wrap(strSpace).putFloat(b);
	        	c.print(strSpace);
	        	if (psHeader.get(i).length() < 12)
	        		spaces(12, strSpace.length);
	        	else
	        		spaces(psHeader.get(i).length(), strSpace.length);
	        }
	    }
	    c.println();
	}
	
	
	//
	// Print
	//
	//  data - this is an array of void *.  This print routine is used by
	//  the QL Layer.
	//
	//  Unfortunately, this is essentially the same as the other Print
	//  routine.
	//
	public void print(PrintStream c, List<byte[]> data) {
		byte[] str= new byte[MAXPRINTSTRING];
		byte[] strSpace= new byte[50];
		int i, a;
		float b;
		
		if (data == null)
	        return;

	    // Increment the number of tuples printed
	    iCount++;

	    for (i = 0; i<attrCount; i++) {
	        if (attributes[i].attrType == AttrType.STRING) {
	            // We will only print out the first MAXNAME+10 characters of
	            // the string value.
	        	Arrays.fill(str, (byte)'0');

	            if (attributes[i].attrLength > MAXPRINTSTRING) {
	            	str= Arrays.copyOf(data.get(i), MAXPRINTSTRING-1);
//	            	System.arraycopy(data, attributes[i].offset, str, 0, MAXPRINTSTRING-1);
	                str[MAXPRINTSTRING-3] ='.';
	                str[MAXPRINTSTRING-2] ='.';
	                c.print(str);
	                spaces(MAXPRINTSTRING, str.length);
	            } else {
	            	str= Arrays.copyOf(data.get(i), attributes[i].attrLength);
//	            	System.arraycopy(data, attributes[i].offset, str, 0, attributes[i].attrLength);
	            	c.print(str);
	            	if (attributes[i].attrLength < psHeader.get(i).length())
	            		spaces(psHeader.get(i).length(), str.length);
	            	else
	            		spaces(attributes[i].attrLength, str.length);
	            }   
	        }   
	        if (attributes[i].attrType == AttrType.INT) {
	        	a= ByteBuffer.wrap(data.get(i)).getInt();
	        	ByteBuffer.wrap(strSpace).putInt(a);
	        	c.print(a);
	        	if (psHeader.get(i).length() < 12)
	        		spaces(12, strSpace.length);
	        	else
	        		spaces(psHeader.get(i).length(), strSpace.length);
	        }
	        if (attributes[i].attrType == AttrType.FLOAT) {
	        	b= ByteBuffer.wrap(data.get(i)).getFloat();
	        	ByteBuffer.wrap(strSpace).putFloat(b);
	        	c.print(strSpace);
	        	if (psHeader.get(i).length() < 12)
	        		spaces(12, strSpace.length);
	        	else
	        		spaces(psHeader.get(i).length(), strSpace.length);
	        }
	    }
	    c.println();
	}
	
	//
	// PrintFooter
	//
	public void printFooter(PrintStream c) {
		c.println();
		c.print(iCount + " tuple(s).\n");
	}

	private DataAttrInfo[] attributes;
	int attrCount;

	// An array of strings for the header information
	List<String> psHeader;
	// Number of spaces between each attribute
	int[] spaces;

	// The number of tuples printed
	int iCount;

}
